# 构造对应的试图
from . import api
from flask import request, jsonify, current_app, session
from ihome.utils.response_code import RET
import re
from ihome import redis_store, db, constants
from ihome.models import User
from sqlalchemy.exc import IntegrityError
from werkzeug.security import generate_password_hash, check_password_hash

@api.route("/users", methods=['GET', 'POST'])
def register():
    if request.method == 'POST':
        """注册
        请求的参数：手机号，短信验证码，密码
        参数格式：json
        """
        # 获取请求的json数据，返回字典
        req_dic = request.get_json()
        mobile = req_dic.get("mobile")
        sms_code = req_dic.get("sms_code")
        password = req_dic.get("password")

        # 参数校验
        if not all([mobile, sms_code, password]):
            return jsonify(errno=RET.PARAMERR, errmsg="参数不完整")
        
        # 判断手机号格式
        # 如果匹配，返回手机号，如果不匹配，返回None
        if not re.match(r"1[34578]\d{9}", mobile):
            # 表示格式不匹配
            return jsonify(errno=RET.PARAMERR, errmsg="手机号格式错误")

        # 从redis中取出验证码，验证验证码的准确性
        try:
            real_sms_code = redis_store.get("sms_code_%s" % mobile)
        except Exception as e:
            return jsonify(errno=RET.DATAERR, errmsg="从redis中读取验证码异常")
        
        if not real_sms_code: 
            real_sms_code = str(real_sms_code, encoding='utf-8')
            if real_sms_code != sms_code:
                return jsonify(errno=RET.DATAERR, errmsg="手机验证码输入错误", real_sms_code=real_sms_code, sms_code=sms_code)

        # 删除redis中的短信验证码，防止重复使用校验
        try:
            redis_store.delete("sms_code_%" % mobile)
        except Exception as e:
            current_app.logger.error(e)

        # 判断验证码是否过期
        if not redis_store.get("sms_code_%s" % mobile):
            return jsonify(errno=RET.DATAERR, errmsg="短信验证码过期")

        
        """  
        在后端中能减少对数据库的操作就减少对数据库的操作
        可以把查询手机号是否被注册过的select的语句和插入语句合成一次
        通过User表中的unique唯一约束来实现
        """
        # # 判断手机号是否注册过
        # try:
        #     user = User.query.filter_by(mobile=mobile).first()  
        # except Exception as e:
        #     current_app.logger.error(e)
        #     return jsonify(errno=RET.DATAERR, errmsg="数据库异常")
        # else:
        #     if user is not None:
        #         return jsonify(errno=RET.DATAEXIST, errmsg="手机号已存在")

        # 保存用户的数据到数据库中, 出现从上往下匹配，具体的放前面
        

        user = User(name=mobile, mobile=mobile)
        user.password = password
        try:
            db.session.add(user)
            db.session.commit()
        except IntegrityError as e:
            # 数据库操作错误后的回滚
            db.session.rollback() 
            # 表示该手机号在MYSQL中重复出现的异常
            current_app.logger.error(e)
            return jsonify(errno=RET.DATAEXIST, errmsg="手机号已经存在")
        except Exception as e:
            return jsonify(error=RET.DATAERR, errmsg="查询数据库异常")
        
        # 保存登录状态到session
        session["name"] = mobile
        session["moblile"] = mobile
        session["user_id"] = user.id
        return jsonify(errno=RET.OK, errmsg="注册成功")

@api.route("/sessions", methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        # 获取参数  方式比较
        # # 获取未经处理过的原始数据而不管内容类型,如果数据格式是json的，则取得的是json字符串，排序和请求参数一致
        # request.get_data()

        # 将请求参数做了处理，得到的是字典格式的，因此排序会打乱依据字典排序规则
        req_dic = request.get_json()

        mobile = req_dic.get("mobile")
        password = req_dic.get("password")

        # 参数完整性检查
        if not all([mobile, password]):
            return jsonify(errno=RET.PARAMERR, errmsg="登录信息填写不完整")

        # # 计算一定时间内的登录次数，如果登录频繁，限制登录
        # try:
        #     redis_store.setex(mobile, constants.SEND_SMS_CODE_INTERVAL, 1)

        # 判断手机号格式是否正确
        # 如果匹配，返回手机号，如果不匹配，返回None
        if not re.match(r"1[34578]\d{9}", mobile):
            # 表示格式不匹配
            return jsonify(errno=RET.PARAMERR, errmsg="手机号格式错误")
        
        
        # 判断错误次数是否超过限制，如果超过，则返回
        user_ip = request.remote_addr
        try:
            access_nums = redis_store.get("access_num_%s" % user_ip)
        except Exception as e:
            current_app.logger.error(e)
        else:
            if access_nums is not None and int(access_nums) >= constants.LOGIN_ERROR_MAX_TIMES:
                return jsonify(errno=RET.REQERR, errmsg="登录过于频繁, 请一分钟后再试")
        
        # 密码是否匹配
        try:
            user_info = User.query.filter_by(mobile=mobile).first()
        except Exception as e:
            current_app.logger.errno(e)
            return jsonify(errno=RET.DBERR, errmsg="登录,数据库查询出错")
        else:
            if user_info.mobile is not None:
                name = user_info.name
                if not user_info.check_password(password):
                    # redis的incr可以对字符串类型的数组数据进行加一操作，如果一开始不存在，则会初始化为1
                    redis_store.incr("access_num_%s" % user_ip)
                    redis_store.expire("access_num_%s" % user_ip, constants.LOGIN_ERROR_FORBID_TIME)
                    return jsonify(errno=RET.DATAERR, errmsg="手机号或密码输入错误")
            else:
                return jsonify(errno=RET.DBERR, errmsg="当前手机号不存在")
                
        session["name"] = name
        session["password"] = password
        session["mobile"] = mobile
        session["user_id"] = user_info.id
        return jsonify(errno=RET.OK, errmsg="登录成功")
        # 登录成功，写入session

@api.route("/session", methods=["GET"])
def check_login():
    """检查登陆状态"""
    # 尝试从session中获取用户的名字
    name = session.get("name")
    mobile = session.get("mobile")

    # 拿到用户的头像URL
    try:
        image_url = User.query.filter_by(id=session.get("user_id")).first().avatar_url
    except Exception as e:
        return jsonify(errno=RET.DBERR, errmsg="用户头像获取失败")
    avatar_url = constants.QINIU_URL_DOMAIN + image_url
    print(avatar_url)
    # 如果session中数据name名字存在，则表示用户已登录，否则未登录
    if name is not None:
        return jsonify(errno=RET.OK, errmsg="true", data={"name": name, "mobile":mobile, "avatar":avatar_url})
    else:
        return jsonify(errno=RET.SESSIONERR, errmsg="false")


@api.route("/session", methods=["DELETE"])
def logout():
    """登出"""
    # 清除session数据
    csrf_token = session.get("csrf_token")
    session.clear()
    session["csrf_token"] = csrf_token
    return jsonify(errno=RET.OK, errmsg="OK")

